﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using ExpertSystem.Exeptions;
using ExpertSystem.Interfaces;
using ExpertSystem.Views;

namespace ExpertSystem.Domains
{
    public class Ruleset : IRuleset
    {
        
        public  Ruleset()
        {
            _rules = new List<IRule>();
            _variables = new List<IVariable>();
            _ships = new List<IShip>();
        }

        public Ruleset(string name,bool isAsync)
        {
            _name = name;
            _rules = new List<IRule>();
            _variables = new List<IVariable>();
            _ships = new List<IShip>();
            _isAsync = isAsync;
        }

        #region Приватные поля
        protected  Stack<IVariable> _stack = new Stack<IVariable>();
        private string _name;
        protected List<IRule> _rules;
        protected List<IVariable> _variables;
        private readonly List<IShip> _ships;
        protected EventHandler Variableinputed;
        //protected Dictionary<IVariable,EventWaitHandle> Dictionary;
        private bool _isAsync;
        #endregion

        public event EventHandler RuleActivated;

        public event EventHandler GoalISFinded;
#region Свойства

        public Stack<IVariable> StackSolution { 
            get
        {
           
            return _stack;
        } }

        public string Name
        {
            get { return _name; }
            private set { _name = value; }
        }
        
        public List<IRule> Rules
        {
            get { return _rules; }
            private set { _rules = value; }
        }
       
        public List<IVariable> Variables
        {
            get { return _variables; }
            private set { _variables = value; }
        }

        public List<IShip> Ships
        {
            get { return _ships; }
        }

        #endregion

        public virtual void CastomizeView(object o, EventArgs eventArgs){}

        public virtual void ActivateRule(object o,EventArgs eventArgs){}

        public void ReadRuleSet(XmlDocument xmlDocument)
        {
            _name = xmlDocument.ChildNodes[0].Attributes[0].Value;
            XmlNode mainNode = xmlDocument.ChildNodes[0];

            foreach (XmlNode objects in mainNode.LastChild.ChildNodes  )
            {
                _ships.Add(new Ship(objects.Attributes[0].Value, objects.Attributes[1].Value, Convert.ToInt32(objects.Attributes[3].Value), Convert.ToInt32(objects.Attributes[2].Value), Convert.ToInt32(objects.Attributes[4].Value)));
            }
            foreach (XmlNode xmlNode in mainNode.ChildNodes[2].ChildNodes)
            {
                var pv = new  List<string>();
                if(xmlNode.Attributes[2].Value != "")
                {
                    foreach (XmlNode childNode in mainNode.ChildNodes[1].ChildNodes)
                    {
                        if (childNode.Attributes[0].Value == xmlNode.Attributes[2].Value)
                        {
                            pv.AddRange(from XmlNode node in childNode.ChildNodes select node.Attributes[0].Value);
                        }
                    }
                }
                var variable = new Variable(xmlNode.ChildNodes[0].ChildNodes.Count != 0,
                                            xmlNode.ChildNodes[1].ChildNodes[0].Value,xmlNode.Attributes[1].Value,pv,_isAsync) { Name = xmlNode.Attributes[0].Value};
                if (xmlNode.ChildNodes[0].ChildNodes.Count != 0)
                    variable.Comment = xmlNode.ChildNodes[0].ChildNodes[0].Value;
                if (variable.Name == mainNode.ChildNodes[0].Attributes[0].Value)
                {
                    variable.IsGoal = true;
                }
                variable.RadyForInput += CastomizeView;
                _variables.Add(variable);
            }

            foreach (XmlNode node in mainNode.ChildNodes[0].ChildNodes)
            {
                var rule = new Rule(node.Attributes[0].Value, node.ChildNodes[2].ChildNodes[0].Value, _variables.First(x => x.Name == node.ChildNodes[1].ChildNodes[0].Attributes[0].Value), node.ChildNodes[1].ChildNodes[0].Attributes[1].Value);
                rule.RuleActivated += ActivateRule;
                _rules.Add(rule);
                
                XmlNode conjuncts = node.ChildNodes[0];
                foreach (XmlNode conjunct in conjuncts.ChildNodes)
                {
                    IFunction function;
                    if(conjunct.Attributes[1].Value == "=")
                    {
                        function = new EqualsFunction();
                    }
                    else
                    {
                        function = new InAreaFunction();
                    }
                    XmlNodeList xmlNodeList = conjunct.ChildNodes;
                    var parametrs = new List<IVariable>();
                    parametrs.Add(_variables.First(variable => variable.Name == conjunct.Attributes[0].Value));
                    parametrs.AddRange((from XmlNode parametr in xmlNodeList
                                        select new Variable(false, "","",null,_isAsync) { IsConstant = true, Value = parametr.Attributes[0].Value }).Cast<IVariable>().ToList());
                    Conjunct con = new Conjunct(
                        _variables.Where(x =>
                            x.Name == conjunct.Attributes[0].Value).ToList()
                            ,function,parametrs);
                    rule.Conjuncts.Add(con);
                }
                
            }

        }

        private void ResolveVariable(IVariable variable, List<IRule> rules)
        {
            var en = rules.GetEnumerator();
            en.MoveNext();
            if (en.Current == null)
            {
                return;
            }

            try
            {
                while (!variable.HaveValue())
                {
                    if (en.Current.CanBeAvtivated())
                    {
                        if (en.Current.CanBeResolved())
                        {
                            en.Current.Resolve();
                            en.MoveNext();
                        }
                        else
                        {
                            if (!_stack.Contains(en.Current.CanNotBeResolvedVariable()))
                                _stack.Push(en.Current.CanNotBeResolvedVariable());
                            ResolveVariable(en.Current.CanNotBeResolvedVariable(), _rules.Where(x => x.IsReternedValueEquals(en.Current.CanNotBeResolvedVariable()) && !x.IsActivated() && x.CanBeAvtivated()).ToList());
                            if (!_stack.Peek().HaveValue())
                            {
                                en.MoveNext();
                            }
                        }
                    }
                    else
                    {
                        en.MoveNext();
                    }

                }
            }
            catch (NullReferenceException)
            {
                throw new SolutionNotFoundExeption();
            }
          
        }

        public void StartConsultation()
        {
            try
            {
                _stack.Push(_variables.First(x => x.IsGoal));
                while (!_variables.First(x => x.IsGoal).HaveValue())
                {
                    if (!_rules.Any(x => x.IsReternedValueEquals(_stack.Peek())))
                    {
                        throw new SystemCanNotBeResolved();
                    }
                    ResolveVariable(_stack.Peek(), _rules.Where(x => x.IsReternedValueEquals(_stack.Peek()) && !x.IsActivated() && x.CanBeAvtivated()).ToList());
                    if (_stack.Peek().HaveValue())
                    {
                        _stack.Pop();
                    }
                }
                GoalISFinded(this, null);
                MessageBox.Show(_variables.First(variable => variable.IsGoal).Value.ToString());

            }
            catch (SolutionNotFoundExeption)
            {
                MessageBox.Show("Решение не найденно.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            
        }
    }
}
